all repos — caroster @ 8d70ecab1b49b1a63ffa379b2fadc2807ff9ea28

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Linkify from 'linkify-react';
  3import Tooltip from '@mui/material/Tooltip';
  4import IconButton from '@mui/material/IconButton';
  5import Box from '@mui/material/Box';
  6import Link from '@mui/material/Link';
  7import Card from '@mui/material/Card';
  8import Container from '@mui/material/Container';
  9import TextField from '@mui/material/TextField';
 10import Typography from '@mui/material/Typography';
 11import TuneIcon from '@mui/icons-material/Tune';
 12import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 13import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
 14import {useTheme} from '@mui/material/styles';
 15import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 16import {PropsWithChildren, useState} from 'react';
 17import {useTranslation} from 'next-i18next';
 18import pageUtils from '../../../lib/pageUtils';
 19import DetailsLink from '../../../containers/DetailsLink';
 20import ShareEvent from '../../../containers/ShareEvent';
 21import PlaceInput from '../../../containers/PlaceInput';
 22import LangSelector from '../../../components/LangSelector';
 23import usePermissions from '../../../hooks/usePermissions';
 24import useEventStore from '../../../stores/useEventStore';
 25import useToastStore from '../../../stores/useToastStore';
 26import EventLayout, {TabComponent} from '../../../layouts/Event';
 27import {
 28  EventByUuidDocument,
 29  useUpdateEventMutation,
 30} from '../../../generated/graphql';
 31import {langLocales} from '../../../locales/constants';
 32
 33interface Props {
 34  eventUUID: string;
 35  announcement?: string;
 36}
 37
 38const Page = (props: PropsWithChildren<Props>) => {
 39  return <EventLayout {...props} Tab={DetailsTab} />;
 40};
 41
 42const DetailsTab: TabComponent<Props> = ({}) => {
 43  const {t} = useTranslation();
 44  const {
 45    userPermissions: {canEditEventDetails},
 46  } = usePermissions();
 47  const theme = useTheme();
 48  const [updateEvent] = useUpdateEventMutation();
 49  const addToast = useToastStore(s => s.addToast);
 50  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 51  const event = useEventStore(s => s.event);
 52  const [isEditing, setIsEditing] = useState(false);
 53
 54  if (!event) return null;
 55
 56  const hasGeoloc = event.latitude && event.longitude;
 57
 58  const onSave = async e => {
 59    try {
 60      const {uuid, ...data} = event;
 61      const {
 62        id,
 63        travels,
 64        waitingPassengers,
 65        __typename,
 66        administrators,
 67        passengers,
 68        ...input
 69      } = data;
 70      await updateEvent({
 71        variables: {
 72          uuid,
 73          eventUpdate: {
 74            ...input,
 75          },
 76        },
 77        refetchQueries: ['eventByUUID'],
 78      });
 79      setIsEditing(false);
 80    } catch (error) {
 81      console.error(error);
 82      addToast(t('event.errors.cant_update'));
 83    }
 84  };
 85
 86  const modifyButton = isEditing ? (
 87    <Tooltip
 88      title={t('event.details.save')}
 89      sx={{
 90        position: 'absolute',
 91        top: theme.spacing(2),
 92        right: theme.spacing(2),
 93      }}
 94    >
 95      <IconButton color="primary" onClick={onSave}>
 96        <CheckCircleOutlineIcon />
 97      </IconButton>
 98    </Tooltip>
 99  ) : (
100    <Tooltip
101      title={t('event.details.modify')}
102      sx={{
103        position: 'absolute',
104        top: theme.spacing(2),
105        right: theme.spacing(2),
106      }}
107    >
108      <IconButton color="primary" onClick={() => setIsEditing(true)}>
109        <TuneIcon />
110      </IconButton>
111    </Tooltip>
112  );
113
114  return (
115    <Box
116      sx={{
117        position: 'relative',
118      }}
119    >
120      <Container
121        sx={{
122          p: 4,
123          mt: 6,
124          mb: 11,
125          mx: 0,
126          [theme.breakpoints.down('md')]: {
127            p: 2,
128          },
129        }}
130      >
131        <Card
132          sx={{
133            position: 'relative',
134            maxWidth: '100%',
135            width: '480px',
136            p: 2,
137          }}
138        >
139          <Typography variant="h4" pb={2}>
140            {t('event.details')}
141          </Typography>
142          {canEditEventDetails() && modifyButton}
143          {(isEditing || event.name) && (
144            <Box pt={2} pr={1.5}>
145              <Typography variant="overline">
146                {t('event.fields.name')}
147              </Typography>
148              <Typography>
149                {isEditing ? (
150                  <TextField
151                    size="small"
152                    fullWidth
153                    value={event.name}
154                    onChange={e => setEventUpdate({name: e.target.value})}
155                    name="name"
156                    id="EditEventName"
157                  />
158                ) : (
159                  <Typography id="EventName">{event.name}</Typography>
160                )}
161              </Typography>
162            </Box>
163          )}
164          {(isEditing || event.date) && (
165            <Box pt={2} pr={1.5}>
166              <Typography variant="overline">
167                {t('event.fields.date')}
168              </Typography>
169              {isEditing ? (
170                <Typography>
171                  <DatePicker
172                    slotProps={{
173                      textField: {
174                        size: 'small',
175                        id: `EditEventDate`,
176                        fullWidth: true,
177                        placeholder: t('event.fields.date_placeholder'),
178                      },
179                    }}
180                    format="DD/MM/YYYY"
181                    value={moment(event.date)}
182                    onChange={date =>
183                      setEventUpdate({
184                        date: !date ? null : moment(date).format('YYYY-MM-DD'),
185                      })
186                    }
187                  />
188                </Typography>
189              ) : (
190                <Box position="relative">
191                  <Typography id="EventDate">
192                    {moment(event.date).format('DD/MM/YYYY')}
193                  </Typography>
194                </Box>
195              )}
196            </Box>
197          )}
198          {(isEditing || event.address) && (
199            <Box pt={2} pr={1.5}>
200              <Typography variant="overline">
201                {t('event.fields.address')}
202              </Typography>
203              {isEditing ? (
204                <PlaceInput
205                  place={event.address}
206                  latitude={event.latitude}
207                  longitude={event.longitude}
208                  onSelect={({place, latitude, longitude}) =>
209                    setEventUpdate({
210                      address: place,
211                      latitude,
212                      longitude,
213                    })
214                  }
215                />
216              ) : (
217                <Box position="relative">
218                  <Typography
219                    id="EventAddress"
220                    title={t`placeInput.noCoordinates`}
221                    sx={{
222                      pr: 3,
223                      display: 'inline-flex',
224                      alignItems: 'center',
225                      columnGap: 1,
226                    }}
227                  >
228                    <Link
229                      target="_blank"
230                      rel="noreferrer"
231                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
232                        event.address
233                      )}`}
234                      onClick={e => e.preventDefault}
235                    >
236                      {event.address}
237                    </Link>
238                    {!hasGeoloc && (
239                      <InfoOutlinedIcon fontSize="small" color="warning" />
240                    )}
241                  </Typography>
242                </Box>
243              )}
244            </Box>
245          )}
246          {(isEditing || event.description) && (
247            <Box pt={2} pr={1.5}>
248              <Typography variant="overline">
249                {t('event.fields.description')}
250              </Typography>
251              {isEditing ? (
252                <Typography>
253                  <TextField
254                    fullWidth
255                    multiline
256                    maxRows={4}
257                    inputProps={{maxLength: 250}}
258                    value={event.description || ''}
259                    onChange={e =>
260                      setEventUpdate({description: e.target.value})
261                    }
262                    id={`EditEventDescription`}
263                    name="description"
264                  />
265                </Typography>
266              ) : (
267                <Typography
268                  id="EventDescription"
269                  sx={{pr: 3, whiteSpace: 'pre-line'}}
270                >
271                  <Linkify options={{render: DetailsLink}}>
272                    {event.description}
273                  </Linkify>
274                </Typography>
275              )}
276            </Box>
277          )}
278          {(isEditing || event.lang) && (
279            <Box pt={2} pr={1.5}>
280              <Typography variant="overline">
281                {t('event.fields.lang')}
282              </Typography>
283              {isEditing ? (
284                <LangSelector
285                  value={event.lang}
286                  onChange={lang => setEventUpdate({lang})}
287                />
288              ) : (
289                <Typography id="EventLang" sx={{pr: 3}}>
290                  {langLocales[event.lang]}
291                </Typography>
292              )}
293            </Box>
294          )}
295          {!isEditing && (
296            <ShareEvent
297              title={`Caroster ${event.name}`}
298              sx={{width: '100%', mt: 2}}
299            />
300          )}
301        </Card>
302      </Container>
303    </Box>
304  );
305};
306
307export const getServerSideProps = pageUtils.getServerSideProps(
308  async (context, apolloClient) => {
309    const {uuid} = context.query;
310    const {host = ''} = context.req.headers;
311    let event = null;
312
313    // Fetch event
314    try {
315      const {data} = await apolloClient.query({
316        query: EventByUuidDocument,
317        variables: {uuid},
318      });
319      event = data?.eventByUUID?.data;
320    } catch (error) {
321      return {
322        notFound: true,
323      };
324    }
325
326    return {
327      props: {
328        eventUUID: uuid,
329        metas: {
330          title: event?.attributes?.name || '',
331          url: `https://${host}${context.resolvedUrl}`,
332        },
333      },
334    };
335  }
336);
337export default Page;